package com.hyt.it.ogt.pj.service.impl;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.it.ogt.pj.enums.CommonEnums;
import com.hyt.it.ogt.pj.common.ResponseCode;
import com.hyt.it.ogt.common.config.GlobalConfig;
import com.hyt.it.ogt.pj.enums.AutoFlagEnums;
import com.hyt.it.ogt.pj.enums.MarkTypeEnums;
import com.hyt.it.ogt.pj.enums.MarkmodelEnums;
import com.hyt.it.ogt.pj.mapper.ItemScoreMapper;
import com.hyt.it.ogt.pj.mapper.PaperMapper;
import com.hyt.it.ogt.pj.mapper.PieceScoreMapper;
import com.hyt.it.ogt.pj.mapper.ProjectMapper;
import com.hyt.it.ogt.pj.mapper.ProjectSettingMapper;
import com.hyt.it.ogt.pj.mapper.SubjectScoreMapper;
import com.hyt.it.ogt.pj.mapper.TaskMapper;
import com.hyt.it.ogt.pj.model.entity.ExamExpert;
import com.hyt.it.ogt.pj.model.entity.ItemScore;
import com.hyt.it.ogt.pj.model.entity.ModeExpert;
import com.hyt.it.ogt.pj.model.entity.Paper;
import com.hyt.it.ogt.pj.model.entity.PieceScore;
import com.hyt.it.ogt.pj.model.entity.Project;
import com.hyt.it.ogt.pj.model.entity.ProjectSetting;
import com.hyt.it.ogt.pj.model.entity.SubjectScore;
import com.hyt.it.ogt.pj.model.entity.Task;
import com.hyt.it.ogt.pj.model.entity.TeacherTaskInfo;
import com.hyt.it.ogt.pj.model.param.ExamExpertSettingParam;
import com.hyt.it.ogt.pj.model.vo.ModeExamExpertVo;
import com.hyt.it.ogt.pj.model.vo.TaskNumberVo;
import com.hyt.it.ogt.pj.model.vo.TaskPieceScoreVo;
import com.hyt.it.ogt.pj.service.ICommonAutomaticAllocationService;
import com.hyt.it.ogt.pj.service.IExamExpertService;
import com.hyt.it.ogt.pj.service.IModeExpertService;
import com.hyt.it.ogt.pj.service.IPieceScoreService;
import com.hyt.it.ogt.pj.service.ITeacherTaskInfoService;
import com.hyt.it.ogt.common.utils.ExecutorPoolUtil;
import com.hyt.loginfo.service.IBaseLoginService;

import cn.hutool.core.thread.ThreadUtil;

/**
 * <p>
 * 评卷项目配置 服务实现类
 * </p>
 *
 * @author liy
 * @since 2022-02-28
 */
@Service
public class CommonAutomaticAllocationServiceImpl extends BaseServiceImpl<ProjectSettingMapper, ProjectSetting> implements ICommonAutomaticAllocationService {
	protected Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private ProjectSettingMapper projectSettingMapper;

	@Autowired
	private ProjectMapper projectMapper;

	@Autowired
	private PieceScoreMapper pieceScoreMapper;

	@Autowired
	private TaskMapper taskMapper;

	@Autowired
	private ItemScoreMapper itemScoreMapper;

	@Autowired
	private IModeExpertService modeExpertService;

	@Autowired
	private IExamExpertService examExpertService;

	@Autowired
	private ITeacherTaskInfoService teacherTaskInfoService;

	@Autowired
	private IPieceScoreService pieceScoreService;

	@Autowired
	private IBaseLoginService iBaseLoginService;

	@Autowired
	private SubjectScoreMapper subjectScoreMapper;

	@Autowired
	private PaperMapper paperMapper;
	
	@Resource
    GlobalConfig globalConfig;
	
	@Resource
	RedisTemplate<String, String> redisTemplate;

	@Override
	public boolean getPaperFlag(String projectId){
		// 试卷包和A\B卷特殊处理，批次id相同，科目id不同
		// 例如 batchid一样，subjectId不一样 or batchId一样，subjectId一样，roomid 不一样
		QueryWrapper<Paper> paperWrapper = new QueryWrapper<>();
		paperWrapper.lambda().in(Paper::getProjectId, projectId);
		List<Paper> papers = paperMapper.selectList(paperWrapper);
		Set<String> subjectIds = papers.stream().map(e -> e.getSubjectId()).collect(Collectors.toSet());
		if(!CollectionUtils.isEmpty(subjectIds) && subjectIds.size() > 1){
			return false;
		}
		return true;
	}


	@Override
	public Integer commonAutomaticAllocation(String projectId, Integer markPublish) throws Exception {
		String lockKey = projectId + "_" + markPublish + "_";
		Project project = projectMapper.selectById(projectId);
		if (null == project) {
			return ResponseCode.MARK_PROJECT_NOT_EMPTY;
		}

		Future<Integer> projectSettingFuture = ExecutorPoolUtil.getPoolTaskExecutor().submit(() -> {
			// zip文件URL
			Integer publishProjectSettingResponseCode = null;
			try {
				publishProjectSettingResponseCode = publishProjectSetting(projectId, markPublish, project, lockKey );
			} catch (Exception e) {
				redisTemplate.delete(lockKey);
				log.error("## publishProjectSetting异常", e);
			}
			return publishProjectSettingResponseCode;
		});
		Integer responCode = null;
		try {
			responCode = ExecutorPoolUtil.getPoolTaskExecutor().submit(() -> {
				// 这里浅浅的睡眠100毫秒避免空转
				while (!projectSettingFuture.isDone()) {
					ThreadUtil.sleep(100);
				}
				return projectSettingFuture.get();
			}).get(globalConfig.getFileTimeOut(), TimeUnit.SECONDS);
		} catch (TimeoutException e) {
			log.error("## publishProjectSetting超过指定等待时间，提前返回", e);
		}
		if (null != responCode) {
			return responCode;
		} else {
			return ResponseCode.PUBLISH_TASK_RUNNING;
		}
	}


	@Transactional(rollbackFor = Exception.class)
    public Integer publishProjectSetting(String projectId, Integer markPublish, Project project, String lockKey) {
		String redisLockValue = redisTemplate.opsForValue().get(lockKey);
		if(projectId.equals(redisLockValue)) {
			return ResponseCode.PUBLISH_TASK_RUNNING;
		}
		// 分配的时候锁定数据五分钟
		redisTemplate.opsForValue().set(lockKey, projectId, 60 * 5L, TimeUnit.SECONDS);
		// 开关关闭，直接执行更新返回
		if (null != markPublish && markPublish.intValue() == CommonEnums.TASK_PUBLISH_STATUS_0.getCode().intValue()) {
			redisTemplate.delete(lockKey);
			return cancelMarkPublish(markPublish, project);
		}

		// 查询项目设置信息
		QueryWrapper<ProjectSetting> wrapper = new QueryWrapper<>();
		wrapper.lambda().eq(ProjectSetting::getProjectId, projectId);
		List<ProjectSetting> projectSettings = projectSettingMapper.selectList(wrapper);
		if (CollectionUtils.isEmpty(projectSettings)) {
			redisTemplate.delete(lockKey);
			return ResponseCode.PROJECT_SETTING_NOT_EMPTY;
		}

		String markModel = projectSettings.get(0).getMarkModel();

		// 获取评卷模式
		List<ModeExamExpertVo> modeExamExpertVos = getModeExamExpertVos(projectId, project.getExamClassfication(),markModel);
		if (CollectionUtils.isEmpty(modeExamExpertVos)) {
			redisTemplate.delete(lockKey);
			return ResponseCode.EXAM_EXPERT_IS_EMPTY;
		}

		// 虚拟考场按批次数据特殊，batchId相同，subjectId或roomId不同，则评卷number只会同一个批次只有一条，所以需要组装把考点和科目都加进去
		Map<String, Integer> numerMap = getNumberMap(markModel, modeExamExpertVos);
		if (CollectionUtils.isEmpty(numerMap)) {
			redisTemplate.delete(lockKey);
			return ResponseCode.MARK_MODEL_NOT_EMPRY;
		}

		//按照“科目or批次or考场”分组评卷模式专家信息
		Map<String, List<ModeExamExpertVo>> examExpertMap = bulidExamExpertMap(markModel, modeExamExpertVos);
		if (CollectionUtils.isEmpty(examExpertMap)) {
			redisTemplate.delete(lockKey);
			return ResponseCode.EXAM_EXPERT_IS_EMPTY;
		}

		// 校验
//		Integer responseCode = checkTotal(projectSettings.get(0), examExpertMap, modeExamExpertVos, tasks);
//		if (!ResponseCode.SUCCESS.equals(responseCode)) {
//			return ResponseCode.EXAM_EXPERT_IS_EMPTY;
//		}

		//按照“科目or批次or考场”找出未分配评卷任务
		ExamExpertSettingParam examExpertSettingParam = new ExamExpertSettingParam();
		examExpertSettingParam.setProjectId(projectId);
		Map<String, List<Task>> tasksMap = buildTaskMap(markModel, examExpertSettingParam);
		if (CollectionUtils.isEmpty(tasksMap)) {
			if (null != markPublish) {
				project.setMarkPublish(markPublish);
				projectMapper.updateById(project);
			}
			redisTemplate.delete(lockKey);
			return ResponseCode.SUCCESS;
		}

		//查询本次评卷的所有评卷专家
		QueryWrapper<ExamExpert> examExpertWrapper = new QueryWrapper<>();
		examExpertWrapper.lambda().eq(ExamExpert::getProjectId, projectId);
		List<ExamExpert> experts = examExpertService.list(examExpertWrapper);
		if (CollectionUtils.isEmpty(experts)) {
			redisTemplate.delete(lockKey);
			return ResponseCode.EXAM_EXPERT_IS_EMPTY;
		}

		//获取客观题得分
		Map<String, SubjectScore> subjectScoreMap = bulidSubjectScoreMap(projectId);

		//获取专家的已评数量
 		Map<String, List<TaskNumberVo>> quantityMap = getQuantityMap(projectId);
		List<TeacherTaskInfo> teacherTaskInfos = new ArrayList<>();
		//开始分配任务，tasksMap为需要分配的任务
		for (Map.Entry<String, List<Task>> entry : tasksMap.entrySet()) {
			if (CollectionUtils.isEmpty(examExpertMap.get(entry.getKey()))) {
				continue;
			}
			//考生的task信息
//			Map<String, Task> taskMap = entry.getValue().stream().collect(Collectors.toMap(Task::getEncodeId, Task -> Task, (key1, key2) -> key2));
			//task的pieceIds集合
			Set<String> pieceIds = entry.getValue().stream().filter(e -> StringUtils.isNotEmpty(e.getPieceId())).map(e -> e.getPieceId()).collect(Collectors.toSet());
			//task的准考证集合
//			List<String> encodeIds = entry.getValue().stream().filter(e -> StringUtils.isNotEmpty(e.getEncodeId())).map(e -> e.getEncodeId()).collect(Collectors.toList());
			//按key取出专家集合
			List<ModeExamExpertVo> examExpertVos = examExpertMap.get(entry.getKey());
			//流水评卷
			if (MarkTypeEnums.MARK_TYPE_FLOW.getCode().equals(projectSettings.get(0).getMarkType())) {

				flowingCalculationRule(examExpertVos, entry.getValue(), quantityMap, subjectScoreMap,markModel);

			} else if (MarkTypeEnums.MARK_TYPE_CROSS.getCode().equals(projectSettings.get(0).getMarkType())) {
				crossCalculationRule(examExpertVos, entry.getValue(), quantityMap, numerMap.get(entry.getKey()), subjectScoreMap,markModel);

			}
			//封装teacherTask数据，为了查看数据的完整性，这张表后面废弃了
			for (ModeExamExpertVo modeExamExpertVo : examExpertVos) {
				for (String pieceId : pieceIds) {
					teacherTaskInfos.add(buildTeacherTaskInfo(modeExamExpertVo.getExpertId(), pieceId, projectId, modeExamExpertVo.getSubjectId(), modeExamExpertVo.getBatchId(),
							modeExamExpertVo.getPlaceId(), modeExamExpertVo.getRoomId()));
				}
			}
		}
		//更新项目表发布状态
		if (null != markPublish) {
			project.setMarkPublish(markPublish);
			projectMapper.updateById(project);
		}
		//统一存储teacherTask数据
		dealTeacherTaskInfo(teacherTaskInfos, projectId);
		redisTemplate.delete(lockKey);
		return ResponseCode.SUCCESS;
	}

	@Override
	public Integer commonAutomaticAllocation(String projectId) {
		// 查询评卷项目
		Project project = projectMapper.selectById(projectId);
		if (null == project) {
			return ResponseCode.MARK_PROJECT_NOT_EMPTY;
		}

		// 如果评卷已经发布，则不做分配
		if (null != project.getMarkPublish() && !CommonEnums.TASK_PUBLISH_STATUS_1.getCode().equals(project.getMarkPublish())) {
			return ResponseCode.PUBLISH_TASK_NOT_SETTING;
		}

		// 获取评卷规则
		ProjectSetting projectSetting = projectSettingMapper.getProjectSettingByProjectId(projectId);
		if (null == projectSetting) {
			return ResponseCode.PROJECT_SETTING_NOT_EMPTY;
		}

		String markModel = projectSetting.getMarkModel();
		// 获取评卷模式
		QueryWrapper<ModeExpert> modeExpertWrapper = new QueryWrapper<>();
		modeExpertWrapper.lambda().eq(ModeExpert::getProjectId, projectId);
		List<ModeExpert> modeExperts = modeExpertService.list(modeExpertWrapper);
		if (CollectionUtils.isEmpty(modeExperts)) {
			return ResponseCode.MARK_MODEL_NOT_EMPRY;
		}
		//自动分配开关开启才会自动分配任务
		if(!AutoFlagEnums.AUTO_FlAG_OPEN.getCode().equals(modeExperts.get(0).getAutoFlag())){
			return ResponseCode.SUCCESS;
		}
		// 评卷专家
		List<ModeExamExpertVo> modeExamExpertVos = getModeExamExpertVos(projectId, project.getExamClassfication(), markModel);
		if (CollectionUtils.isEmpty(modeExamExpertVos)) {
			return ResponseCode.EXAM_EXPERT_IS_EMPTY;
		}
		//设置的专家评卷数
		Map<String, Integer> numerMap = getNumberMap(markModel, modeExamExpertVos);
		if (CollectionUtils.isEmpty(numerMap)) {
			return ResponseCode.MARK_MODEL_NOT_EMPRY;
		}
		// 分组评卷专家模式
		Map<String, List<ModeExamExpertVo>> examExpertMap = bulidExamExpertMap(markModel, modeExamExpertVos);
		if (CollectionUtils.isEmpty(examExpertMap)) {
			return ResponseCode.EXAM_EXPERT_IS_EMPTY;
		}

		// 分组未评卷考生任务
		ExamExpertSettingParam examExpertSettingParam = new ExamExpertSettingParam();
		examExpertSettingParam.setProjectId(projectId);
		Map<String, List<Task>> tasksMap = buildTaskMap(markModel, examExpertSettingParam);
		if (CollectionUtils.isEmpty(tasksMap)) {
			return ResponseCode.SUCCESS;
		}
		//评卷专家
		QueryWrapper<ExamExpert> examExpertWrapper = new QueryWrapper<>();
		examExpertWrapper.lambda().eq(ExamExpert::getProjectId, projectId);
		List<ExamExpert> experts = examExpertService.list(examExpertWrapper);
		if (CollectionUtils.isEmpty(experts)) {
			return ResponseCode.EXAM_EXPERT_IS_EMPTY;
		}

		//客观题得分
		Map<String, SubjectScore> subjectScoreMap = bulidSubjectScoreMap(projectId);

		// 开启自动分配第一次评卷quantity为0，后面再用quantity+size
		// 封装所以已经分配的任务数
		Map<String, List<TaskNumberVo>> quantityMap = getQuantityMap(projectId);
		Map<String, ExamExpert> expertMap = experts.stream().collect(Collectors.toMap(ExamExpert::getId, ExamExpert -> ExamExpert, (key1, key2) -> key2));
		List<TeacherTaskInfo> teacherTaskInfos = new ArrayList<>();
		//开始分配任务，tasksMap为需要分配的任务
		for (Map.Entry<String, List<Task>> entry : tasksMap.entrySet()) {
			if (CollectionUtils.isEmpty(examExpertMap.get(entry.getKey()))) {
				continue;
			}
			//考生的task信息
//			Map<String, Task> taskMap = entry.getValue().stream().collect(Collectors.toMap(Task::getEncodeId, Task -> Task, (key1, key2) -> key2));
			//task的准考证集合
//			List<String> encodeIds = entry.getValue().stream().filter(e -> StringUtils.isNotEmpty(e.getEncodeId())).map(e -> e.getEncodeId()).collect(Collectors.toList());
			//task的pieceIds集合
			Set<String> pieceIds = entry.getValue().stream().filter(e -> StringUtils.isNotEmpty(e.getPieceId())).map(e -> e.getPieceId()).collect(Collectors.toSet());
			//获取分组的专家
			List<ModeExamExpertVo> examExpertVos = examExpertMap.get(entry.getKey());
			//流水评卷
			if (MarkTypeEnums.MARK_TYPE_FLOW.getCode().equals(projectSetting.getMarkType())) {
				//自动分配
				if (AutoFlagEnums.AUTO_FlAG_OPEN.getCode().equals(examExpertVos.get(0).getAutoFlag())) {
					flowingAutoCalculationRule( examExpertVos, entry.getValue(), expertMap, subjectScoreMap);
				} else {
					flowingCalculationRule( examExpertVos, entry.getValue(), quantityMap, subjectScoreMap,markModel);
				}
			} else if (MarkTypeEnums.MARK_TYPE_CROSS.getCode().equals(projectSetting.getMarkType())) {
 				//自动分配
				if (AutoFlagEnums.AUTO_FlAG_OPEN.getCode().equals(examExpertVos.get(0).getAutoFlag())) {
					crossAutoCalculationRule(examExpertVos, entry.getValue(), numerMap.get(entry.getKey()), expertMap, quantityMap, subjectScoreMap,markModel);
				} else {
					crossCalculationRule( examExpertVos, entry.getValue(), quantityMap, numerMap.get(entry.getKey()), subjectScoreMap, markModel);
				}
			}
			for (ModeExamExpertVo modeExamExpertVo : examExpertVos) {
				for (String pieceId : pieceIds) {
					teacherTaskInfos.add(buildTeacherTaskInfo(modeExamExpertVo.getExpertId(), pieceId, projectId, modeExamExpertVo.getSubjectId(), modeExamExpertVo.getBatchId(),
							modeExamExpertVo.getPlaceId(), modeExamExpertVo.getRoomId()));
				}
			}
		}
		dealTeacherTaskInfo(teacherTaskInfos, projectId);
		return ResponseCode.SUCCESS;
	}

	private Integer cancelMarkPublish(Integer markPublish, Project project) {
		project.setMarkPublish(markPublish);
		projectMapper.updateById(project);
		// 查询出已经分配且专家未评卷任务
		List<TaskPieceScoreVo> tasks = taskMapper.queryPieceScoreTask(project.getId());
		if (CollectionUtils.isEmpty(tasks)) {
			return ResponseCode.SUCCESS;
		}
		Set<String> ids = tasks.stream().filter(e -> StringUtils.isNotEmpty(e.getId())).map(e -> e.getId()).collect(Collectors.toSet());
		// 删除未评卷的评卷块数据
		pieceScoreMapper.deleteBatchIds(ids);
		// 查找出已经有部分评卷的PieceId
		List<TaskPieceScoreVo> alreadTasks = taskMapper.queryAlreadyPieceScoreTask(project.getId());
		Map<String, TaskPieceScoreVo> taskPieceScoreMap = alreadTasks.stream()
				.collect(Collectors.toMap(t -> t.getTaskId() + t.getUserId() + t.getEncodeId(), TaskPieceScoreVo -> TaskPieceScoreVo, (key1, key2) -> key2));

		List<TaskPieceScoreVo> diffVos = new ArrayList<>();
		// 还未评卷
		if (CollectionUtils.isEmpty(taskPieceScoreMap)) {
			diffVos.addAll(tasks);
		} else {
			for (TaskPieceScoreVo vo : tasks) {
				// 找出有交集的任务，去除掉
				if (null != taskPieceScoreMap.get(vo.getTaskId() + vo.getUserId() + vo.getEncodeId())) {
					diffVos.add(vo);
				}
			}
		}
		// 删除这批分配给专家的任务
		if (!CollectionUtils.isEmpty(diffVos)) {
			Set<String> pieceIds = diffVos.stream().filter(e -> StringUtils.isNotEmpty(e.getPieceId())).map(e -> e.getPieceId()).collect(Collectors.toSet());
			Set<String> encodeIds = diffVos.stream().filter(e -> StringUtils.isNotEmpty(e.getEncodeId())).map(e -> e.getEncodeId()).collect(Collectors.toSet());
			Set<String> userIds = diffVos.stream().filter(e -> StringUtils.isNotEmpty(e.getUserId())).map(e -> e.getUserId()).collect(Collectors.toSet());
			QueryWrapper<TeacherTaskInfo> teacherTaskInfoWrapper = new QueryWrapper<>();
			teacherTaskInfoWrapper.lambda().eq(TeacherTaskInfo::getProjectId, project.getId()).in(TeacherTaskInfo::getUserId, userIds).in(TeacherTaskInfo::getPieceCode, pieceIds);
			teacherTaskInfoService.remove(teacherTaskInfoWrapper);
			// 删除掉保存的题目得分
			QueryWrapper<ItemScore> itemScoreWrapper = new QueryWrapper<>();
			itemScoreWrapper.lambda().in(ItemScore::getPieceId, pieceIds).in(ItemScore::getUserId, userIds).in(ItemScore::getEncodeId, encodeIds).in(ItemScore::getUserId, userIds);
			itemScoreMapper.delete(itemScoreWrapper);

		}
		return ResponseCode.SUCCESS;
	}

	private List<ModeExamExpertVo> getModeExamExpertVos(String projectId, Integer examClassfication, String markModel) {

		if (MarkmodelEnums.MARK_MODEL_SUBJECT.getCode().equals(markModel)) {
			return examExpertService.queryEaxmModeExamExpertVos(projectId,markModel,null,null);
		}else if( MarkmodelEnums.MARK_MODEL_BATCH.getCode().equals(markModel)){
			return examExpertService.queryEaxmModeExamExpertVos(projectId,markModel,markModel,null);
		}else if( MarkmodelEnums.MARK_MODEL_ROOM.getCode().equals(markModel)){
			return examExpertService.queryEaxmModeExamExpertVos(projectId,markModel,markModel,markModel);
		}else{
			return examExpertService.queryExamExpertVos(projectId);
		}

	}


	private Map<String, Integer> getNumberMap(String markModel, List<ModeExamExpertVo> modeExperts) {
		Map<String, Integer> numerMap = new HashMap<>();
		if (MarkmodelEnums.MARK_MODEL_SUBJECT.getCode().equals(markModel)) {
			numerMap = modeExperts.stream().filter(e -> StringUtils.isNotEmpty(e.getSubjectId()))
					.collect(Collectors.toMap(e -> e.getSubjectId() , ModeExamExpertVo::getNumber, (key1, key2) -> key2));

		}
		if (MarkmodelEnums.MARK_MODEL_BATCH.getCode().equals(markModel)) {
			numerMap = modeExperts.stream().filter(e -> StringUtils.isNotEmpty(e.getBatchId()))
					.collect(Collectors.toMap(e ->  e.getBatchId() , ModeExamExpertVo::getNumber, (key1, key2) -> key2));

		}
		if (MarkmodelEnums.MARK_MODEL_ROOM.getCode().equals(markModel)) {
			numerMap = modeExperts.stream().filter(e -> StringUtils.isNotEmpty(e.getRoomId()))
					.collect(Collectors.toMap(e ->  e.getRoomId(), ModeExamExpertVo::getNumber, (key1, key2) -> key2));

		}
		return numerMap;
	}



	private Map<String, List<TaskNumberVo>> getQuantityMap(String projectId) {
		List<TaskNumberVo> taskNumberVos = taskMapper.queryTaskNumberVo(projectId);
		Map<String, List<TaskNumberVo>> quantityMap  = taskNumberVos.stream().collect(Collectors.groupingBy(t -> t.getExpertId()));
		return quantityMap;
	}

	private Map<String, SubjectScore> bulidSubjectScoreMap(String projectId) {
		QueryWrapper<SubjectScore> subjectScoreWrapper = new QueryWrapper<>();
		subjectScoreWrapper.lambda().eq(SubjectScore::getProjectId, projectId);
		List<SubjectScore> subjectScores = subjectScoreMapper.selectList(subjectScoreWrapper);
		return subjectScores.stream().collect(Collectors.toMap(s -> s.getSubjectId() + s.getEncodeId(), SubjectScore -> SubjectScore, (key1, key2) -> key2));
	}

	private void dealTeacherTaskInfo(List<TeacherTaskInfo> teacherTaskInfos, String projectId) {
		Set<String> pieceCodes = teacherTaskInfos.stream().filter(e -> StringUtils.isNotEmpty(e.getPieceCode())).map(e -> e.getPieceCode()).collect(Collectors.toSet());
		QueryWrapper<TeacherTaskInfo> teacherTaskInfoWrapper = new QueryWrapper<>();
		teacherTaskInfoWrapper.lambda().eq(TeacherTaskInfo::getProjectId, projectId).in(TeacherTaskInfo::getPieceCode, pieceCodes);
		// 查询出已经存在的任务
		List<TeacherTaskInfo> alreadyTeacherTaskInfos = teacherTaskInfoService.list(teacherTaskInfoWrapper);
		Map<String, String> teacherTaskInfoMap = alreadyTeacherTaskInfos.stream()
				.collect(Collectors.toMap(e -> e.getPieceCode() + e.getSubjectId() + e.getBatchId() + e.getPlaceId() + e.getRoomId() + e.getUserId(), TeacherTaskInfo::getId, (key1, key2) -> key2));
		List<TeacherTaskInfo> addTeacherTaskInfos = new ArrayList<>();
		// 过滤掉已经存在的任务
		for (TeacherTaskInfo teacherTaskInfo : teacherTaskInfos) {
			if (!CollectionUtils.isEmpty(teacherTaskInfoMap) && null != teacherTaskInfoMap.get(teacherTaskInfo.getPieceCode() + teacherTaskInfo.getSubjectId() + teacherTaskInfo.getBatchId()
					+ teacherTaskInfo.getPlaceId() + teacherTaskInfo.getRoomId() + teacherTaskInfo.getUserId())) {
				continue;
			}
			addTeacherTaskInfos.add(teacherTaskInfo);
		}
		if (!CollectionUtils.isEmpty(addTeacherTaskInfos)) {
			teacherTaskInfoService.saveBatch(addTeacherTaskInfos);

		}
	}

	private Map<String, List<Task>> buildTaskMap(String markModel, ExamExpertSettingParam examExpertSettingParam) {
		// 如果自动分配标识打开且还有考生任务则重新平均给专家分配任务，变更专家的评卷数
		// 找出未分配考生
//		List<Task> tasksList = taskMapper.queryTaskPieceScore(examExpertSettingParam);
		// 找出任务
		List<Task> tasksList = taskMapper.queryTaskByExamExpertSetting(examExpertSettingParam);
		if (CollectionUtils.isEmpty(tasksList)) {
			return null;
		}
		Map<String, List<Task>> tasksMap = null;
		if (MarkmodelEnums.MARK_MODEL_SUBJECT.getCode().equals(markModel)) {
			tasksMap = tasksList.stream().filter(e -> StringUtils.isNotEmpty(e.getSubjectId())).collect(Collectors.groupingBy(c -> c.getSubjectId()));

		}
		if (MarkmodelEnums.MARK_MODEL_BATCH.getCode().equals(markModel)) {
			tasksMap = tasksList.stream().filter(e -> StringUtils.isNotEmpty(e.getBatchId())).collect(Collectors.groupingBy(c ->  c.getBatchId() ));

		}
		if (MarkmodelEnums.MARK_MODEL_ROOM.getCode().equals(markModel)) {
			tasksMap = tasksList.stream().filter(e -> StringUtils.isNotEmpty(e.getRoomId())).collect(Collectors.groupingBy(c -> c.getRoomId()));

		}
		return tasksMap;
	}

	/**
	 * 封装专家map
	 *
	 * @param markModel
	 * @return
	 */
	private Map<String, List<ModeExamExpertVo>> bulidExamExpertMap(String markModel, List<ModeExamExpertVo> modeExamExpertVos) {

		Map<String, List<ModeExamExpertVo>> examExpertMap = new HashMap<>();
		if (MarkmodelEnums.MARK_MODEL_SUBJECT.getCode().equals(markModel)) {
			examExpertMap = modeExamExpertVos.stream().filter(e -> StringUtils.isNotEmpty(e.getSubjectId())).collect(Collectors.groupingBy(c -> c.getSubjectId() ));
		}
		if (MarkmodelEnums.MARK_MODEL_BATCH.getCode().equals(markModel)) {
			examExpertMap = modeExamExpertVos.stream().filter(e -> StringUtils.isNotEmpty(e.getBatchId())).collect(Collectors.groupingBy(c -> c.getBatchId() ));
		}
		if (MarkmodelEnums.MARK_MODEL_ROOM.getCode().equals(markModel)) {
			examExpertMap = modeExamExpertVos.stream().filter(e -> StringUtils.isNotEmpty(e.getRoomId())).collect(Collectors.groupingBy(c ->  c.getRoomId()));

		}
		return examExpertMap;
	}

	/**
	 * 校验评卷总量：校验列表中专家的评卷量总和是否等于评卷总量，若不等于，则提示“专家评卷量总和不等于评卷总量，请调整！
	 *
	 * @param projectSetting
	 * @param examExpertMap
	 * @param modeExamExpertVos
	 * @param tasks
	 * @return
	 */
	private Integer checkTotal(ProjectSetting projectSetting, Map<String, List<ModeExamExpertVo>> examExpertMap, List<ModeExamExpertVo> modeExamExpertVos, List<Task> tasks) {
		for (ModeExamExpertVo modeExpert : modeExamExpertVos) {
			List<ModeExamExpertVo> examExperts = null;
			Integer taskCount = null;
			if (MarkmodelEnums.MARK_MODEL_SUBJECT.getCode().equals(projectSetting.getMarkModel())) {
				examExperts = examExpertMap.get(modeExpert.getSubjectId() );
				taskCount = tasks.stream().filter(e -> StringUtils.isNotEmpty(e.getSubjectId())).collect(Collectors.groupingBy(c -> c.getSubjectId())).get(modeExpert.getSubjectId()).size();
			}
			if (MarkmodelEnums.MARK_MODEL_BATCH.getCode().equals(projectSetting.getMarkModel())) {
				examExperts = examExpertMap.get( modeExpert.getBatchId() );
				taskCount = tasks.stream().filter(e -> StringUtils.isNotEmpty(e.getBatchId())).collect(Collectors.groupingBy(c -> c.getBatchId())).get(modeExpert.getBatchId()).size();
			}
			if (MarkmodelEnums.MARK_MODEL_ROOM.getCode().equals(projectSetting.getMarkModel())) {
				examExperts = examExpertMap.get(modeExpert.getRoomId());
				taskCount = tasks.stream().filter(e -> StringUtils.isNotEmpty(e.getRoomId())).collect(Collectors.groupingBy(c -> c.getRoomId())).get(modeExpert.getRoomId()).size();
			}

			if (null == taskCount) {
				return ResponseCode.PROJECT_TASK_NOT_EMPTY;
			}
			if (CollectionUtils.isEmpty(examExperts)) {
				return ResponseCode.EXAM_EXPERT_IS_EMPTY;
			}
			if (MarkTypeEnums.MARK_TYPE_FLOW.getCode().equals(projectSetting.getMarkType())) {
				Integer total = examExperts.stream().filter(e -> null != e.getQuantity()).collect(Collectors.summingInt(ModeExamExpertVo::getQuantity));
				if (null == total || total.intValue() < taskCount.intValue()) {
					return ResponseCode.MARK_TOTAL_NOT_TOTAL_NUMBER;
				}
			} else if (MarkTypeEnums.MARK_TYPE_CROSS.getCode().equals(projectSetting.getMarkType())) {
				Integer total = examExperts.stream().filter(e -> null != e.getQuantity()).collect(Collectors.summingInt(ModeExamExpertVo::getQuantity));
				if (null == total || total.intValue() < (taskCount.intValue() * modeExpert.getNumber())) {
					return ResponseCode.MARK_TOTAL_NOT_TOTAL_NUMBER;
				}

			}
		}
		return ResponseCode.SUCCESS;
	}

	/**
	 * 交叉评卷分配专家任务规则
	 *
	 * @param tasks
	 * @param examExpertVos
	 * @param
	 *
	 */
	private void crossCalculationRule(  List<ModeExamExpertVo> examExpertVos, List< Task> tasks, Map<String, List<TaskNumberVo>> quantityMap, Integer number,
									  Map<String, SubjectScore> subjectScoreMap,String markModel) {

		List<PieceScore> pieceScores = new ArrayList<>();
		List<Task> allTasks = new ArrayList<>();
		//将能看最多任务的老师排在前面
		examExpertVos.sort(Comparator.comparing(ModeExamExpertVo::getQuantity).reversed());
		//需要分配的总任务数
		for(int i=0 ; i< number ; i++){
			allTasks.addAll(tasks);
		}
		for (ModeExamExpertVo examExpert : examExpertVos) {
			//查询出专家已经评卷的多少份，用设置的Quantity减去已经评卷的数量
			if (!CollectionUtils.isEmpty(quantityMap) && !CollectionUtils.isEmpty(quantityMap.get(examExpert.getExpertId()) )
					&& null != examExpert.getQuantity()) {
				//quantityMap统计是project专家的总已评卷数，未区分考场和科目。存在project多考场or多科目分配给同一个专家
 				int  quantity = getQuantity(quantityMap.get(examExpert.getExpertId()),examExpert,markModel);
				examExpert.setQuantity(examExpert.getQuantity().intValue() -  quantity) ;
			}
			Map<String, String> countMap = new HashMap<>();
			Iterator<Task> iterator = allTasks.iterator();
			while (iterator.hasNext()){
				//老师的评卷数量已经全部分配
				if( examExpert.getQuantity().intValue() == 0){
					break;
				}
				Task task = iterator.next();
				//如果给当前专家已经分配了改任务，则不重复分配
				if(StringUtils.isNotEmpty(countMap.get(task.getId()))){
					continue;
				}
				//专家已经评卷了当前任务则不再重复分配给改专家
				if(!CollectionUtils.isEmpty(quantityMap) && !CollectionUtils.isEmpty(quantityMap.get(examExpert.getExpertId()))){
					Set<String> taskIds = quantityMap.get(examExpert.getExpertId()).stream().map(t -> t.getTaskId()).collect(Collectors.toSet());
 					if(taskIds.contains(task.getId())){
						continue;
					}
				}
				pieceScores.add(buildPieceScores(examExpert.getExpertId(), task, subjectScoreMap));
				countMap.put(task.getId(),task.getId());
				iterator.remove();
				//分配好一个则将examExpert.getQuantity() - 1
				examExpert.setQuantity(examExpert.getQuantity() - 1);
			}
		}
		pieceScoreService.saveBatch(pieceScores);
	}

	/**
	 * 交叉评卷自动分配专家任务规则
	 *
	 * @param
	 * @param examExpertVos
	 * @param
	 *
	 */
	private void crossAutoCalculationRule( List<ModeExamExpertVo> examExpertVos, List<Task> tasks, Integer number, Map<String, ExamExpert> expertMap,
										  Map<String, List<TaskNumberVo>> quantityMap, Map<String, SubjectScore> subjectScoreMap,String markModel) {
		//每个老师的评卷任务
		List<PieceScore> pieceScores = new ArrayList<>();
		//将能看最多任务的老师排在前面
		examExpertVos.sort(Comparator.comparing(ModeExamExpertVo::getQuantity).reversed());
		for (Task task : tasks) {
			int count = 0;
			for (ModeExamExpertVo modeExamExpertVo : examExpertVos) {
				//专家已经评卷了当前任务则不再重复分配给改专家
				if(!CollectionUtils.isEmpty(quantityMap) && !CollectionUtils.isEmpty(quantityMap.get(modeExamExpertVo.getExpertId()))){
					//quantityMap统计是project专家的总已评卷数，未区分考场和科目。存在project多考场or多科目分配给同一个专家
					int  quantity = getQuantity(quantityMap.get(modeExamExpertVo.getExpertId()),modeExamExpertVo,markModel);
					if(quantity > 0){
						Set<String> taskIds = quantityMap.get(modeExamExpertVo.getExpertId()).stream().map(t -> t.getTaskId()).collect(Collectors.toSet());
						if(taskIds.contains(task.getId())){
							continue;
						}
					}
				}
				//当前任务的分配次数大于评卷设置的评卷次数
				if (count >= number) {
					break;
				}
				pieceScores.add(buildPieceScores(modeExamExpertVo.getExpertId(),task, subjectScoreMap));
				count++;
			}
		}

		//重新计算专家的评卷数
		Map<String, List<PieceScore>> pieceScoreMap = pieceScores.stream().collect(Collectors.groupingBy(c -> c.getUserId()));
		List<ExamExpert> examExperts = new ArrayList<>();
		for (ModeExamExpertVo modeExamExpertVo : examExpertVos) {
			if (CollectionUtils.isEmpty(pieceScoreMap) || CollectionUtils.isEmpty(pieceScoreMap.get(modeExamExpertVo.getExpertId()))) {
				continue;
			}
			if (CollectionUtils.isEmpty(expertMap) || null == expertMap.get(modeExamExpertVo.getId())) {
				continue;
			}
			ExamExpert examExpert = expertMap.get(modeExamExpertVo.getId());
			if (CollectionUtils.isEmpty(quantityMap) ||CollectionUtils.isEmpty(quantityMap.get(examExpert.getExpertId()))
					|| quantityMap.get(examExpert.getExpertId()).size() == 0) {
				examExpert.setQuantity(pieceScoreMap.get(examExpert.getExpertId()).size());
			} else {
				examExpert.setQuantity(examExpert.getQuantity() + pieceScoreMap.get(examExpert.getExpertId()).size());

			}
			examExperts.add(examExpert);

		}
		pieceScoreService.saveBatch(pieceScores);
		examExpertService.updateBatchById(examExperts);

	}

	/**
	 * 流水评卷分配专家任务规则
	 *
	 * @param
	 * @param examExpertVos
	 * @param
	 *
	 */
	private void flowingCalculationRule(List<ModeExamExpertVo> examExpertVos, List< Task> tasks, Map<String, List<TaskNumberVo>> quantityMap,
										Map<String, SubjectScore> subjectScoreMap,String markModel) {
		List<PieceScore> pieceScores = new ArrayList<>(tasks.size());
		//将能看最多任务的老师排在前面
		examExpertVos.sort(Comparator.comparing(ModeExamExpertVo::getQuantity).reversed());
		for (ModeExamExpertVo examExpert : examExpertVos) {
			// 查询出专家已经评卷的多少份，用设置的Quantity减去已经评卷的数量
			if (!CollectionUtils.isEmpty(quantityMap) && !CollectionUtils.isEmpty(quantityMap.get(examExpert.getExpertId()))) {
				//quantityMap统计是project专家的总已评卷数，未区分考场和科目。存在project多考场or多科目分配给同一个专家
//				 int  quantity =  quantityMap.get(examExpert.getExpertId()).size();
				int  quantity = getQuantity(quantityMap.get(examExpert.getExpertId()),examExpert,markModel);
                 examExpert.setQuantity(examExpert.getQuantity().intValue() - quantity);
 			}
			//总任务
			Iterator<Task> iterator = tasks.iterator();
			while (iterator.hasNext()){
				//老师的评卷数量已经全部分配
				if( examExpert.getQuantity().intValue() == 0){
					break;
				}
				Task task = iterator.next();
				pieceScores.add(buildPieceScores(examExpert.getExpertId(), task, subjectScoreMap));
				iterator.remove();
				//分配好一个则将examExpert.getQuantity() - 1
				examExpert.setQuantity(examExpert.getQuantity() - 1);
			}
		}
		//分配好任务存入pieceScore
		pieceScoreService.saveBatch(pieceScores);

	}


	private  int  getQuantity(List<TaskNumberVo> taskNumberVos,ModeExamExpertVo examExpert,String markModel ){
		int quantity  = 0;
		if (MarkmodelEnums.MARK_MODEL_SUBJECT.getCode().equals(markModel)) {
			for (TaskNumberVo taskNumberVo :taskNumberVos) {
				if(examExpert.getExpertId().equals(taskNumberVo.getExpertId()) && examExpert.getSubjectId().equals(taskNumberVo.getSubjectId())){
					quantity ++;
				}
			}
 		}
		if (MarkmodelEnums.MARK_MODEL_BATCH.getCode().equals(markModel)) {
			for (TaskNumberVo taskNumberVo :taskNumberVos) {
				if(examExpert.getExpertId().equals(taskNumberVo.getExpertId()) && examExpert.getBatchId().equals(taskNumberVo.getBatchId())){
					quantity ++;
				}
			}
		}
		if (MarkmodelEnums.MARK_MODEL_ROOM.getCode().equals(markModel)) {
			for (TaskNumberVo taskNumberVo :taskNumberVos) {
				if(examExpert.getExpertId().equals(taskNumberVo.getExpertId()) && examExpert.getRoomId().equals(taskNumberVo.getRoomId())){
					quantity ++;
				}
			}
		}
		return  quantity;
	}

	/**
	 * 流水评卷自动分配专家任务规则
	 *
	 * @param
	 * @param modeExamExpertVos
	 * @param
	 *
	 */
	private void flowingAutoCalculationRule( List<ModeExamExpertVo> modeExamExpertVos, List<Task> tasks, Map<String, ExamExpert> expertMap,
											 Map<String, SubjectScore> subjectScoreMap) {
		List<PieceScore> pieceScores = new ArrayList<>();
		//平均分配的评卷任务都是一个一个的推送过来的
		// 将能看最多任务的专家排在前面
		modeExamExpertVos.sort(Comparator.comparing(ModeExamExpertVo::getQuantity).reversed());
		for (Task task : tasks) {
			//专家已经评卷了当前任务则不再重复分配给改专家
			pieceScores.add(buildPieceScores(modeExamExpertVos.get(0).getExpertId(), task, subjectScoreMap));
		}
		// 更新专家的评卷数
		modeExamExpertVos.get(0).setQuantity(modeExamExpertVos.get(0).getQuantity() + 1);

		//重新计算专家的评卷数
		List<ExamExpert> examExperts = new ArrayList<>();
		for (ModeExamExpertVo modeExamExpertVo : modeExamExpertVos) {
			if (CollectionUtils.isEmpty(expertMap) || null == expertMap.get(modeExamExpertVo.getId())) {
				continue;
			}
			ExamExpert examExpert = expertMap.get(modeExamExpertVo.getId());
			examExpert.setQuantity(modeExamExpertVo.getQuantity());
			examExperts.add(examExpert);
		}
		examExpertService.updateBatchById(examExperts);
		pieceScoreService.saveBatch(pieceScores);
	}

	private PieceScore buildPieceScores(String expertId, Task task, Map<String, SubjectScore> subjectScoreMap) {
		PieceScore pieceScore = new PieceScore();
		pieceScore.setEncodeId(task.getEncodeId());
		pieceScore.setPieceId(task.getPieceId());
		pieceScore.setTaskId(task.getId());
		pieceScore.setSubjectId(task.getSubjectId());

		if (!CollectionUtils.isEmpty(subjectScoreMap) && StringUtils.isNotEmpty(pieceScore.getSubjectId()) && null != subjectScoreMap.get(pieceScore.getSubjectId() + task.getEncodeId())) {
			pieceScore.setObjectiveScore(subjectScoreMap.get(pieceScore.getSubjectId() + task.getEncodeId()).getObjectiveScore());
		}

		pieceScore.setFinish(CommonEnums.TASK_MARK_STATUS_0.getCode());
		pieceScore.setMarkTimes(CommonEnums.MARK_STATUS_0.getCode());
		pieceScore.setUserId(expertId);
		pieceScore.setCreateBy(iBaseLoginService.getSysUser().getLoginname());
		pieceScore.setCreateDate(LocalDateTime.now());
		pieceScore.setUpdateBy(iBaseLoginService.getSysUser().getLoginname());
		pieceScore.setUpdateDate(LocalDateTime.now());
		return pieceScore;
	}

	private TeacherTaskInfo buildTeacherTaskInfo(String expertId, String pieceId, String projectId, String subjectId, String batchId, String placeId, String roomId) {
		TeacherTaskInfo teacherTaskInfo = new TeacherTaskInfo();
		teacherTaskInfo.setProjectId(projectId);
		teacherTaskInfo.setSubjectId(subjectId);
		teacherTaskInfo.setPieceCode(pieceId);
		teacherTaskInfo.setUserId(expertId);
		teacherTaskInfo.setBatchId(batchId);
		teacherTaskInfo.setPlaceId(placeId);
		teacherTaskInfo.setRoomId(roomId);
		teacherTaskInfo.setCreateBy(iBaseLoginService.getSysUser().getLoginname());
		teacherTaskInfo.setCreateDate(LocalDateTime.now());
		teacherTaskInfo.setUpdateBy(iBaseLoginService.getSysUser().getLoginname());
		teacherTaskInfo.setUpdateDate(LocalDateTime.now());
		return teacherTaskInfo;
	}

}
